/*
 * Copyright (C) 2010 Interactive Media Management
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package dk.i2m.converge.ejb.facades;

import dk.i2m.converge.core.search.SearchEngineIndexingException;
import dk.i2m.converge.core.workflow.EditionActionException;
import dk.i2m.converge.ejb.services.DataNotFoundException;
import dk.i2m.converge.core.workflow.Department;
import dk.i2m.converge.core.workflow.Edition;
import dk.i2m.converge.core.workflow.EditionPattern;
import dk.i2m.converge.core.workflow.Outlet;
import dk.i2m.converge.core.workflow.OutletEditionAction;
import dk.i2m.converge.core.workflow.Section;
import dk.i2m.converge.core.content.NewsItem;
import dk.i2m.converge.core.security.UserAccount;
import dk.i2m.converge.ejb.services.ConfigurationServiceLocal;
import dk.i2m.converge.ejb.services.DaoServiceLocal;
import dk.i2m.converge.ejb.services.NotificationServiceLocal;
import dk.i2m.converge.ejb.services.QueryBuilder;
import dk.i2m.converge.ejb.services.UserServiceLocal;
import dk.i2m.converge.core.plugin.EditionAction;
import dk.i2m.converge.core.plugin.PluginContext;
import dk.i2m.converge.utils.CalendarUtils;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Resource;
import javax.ejb.EJB;
import javax.ejb.SessionContext;
import javax.ejb.Stateless;

/**
 * Stateless session bean providing a facade to working with {@link Outlet}s.
 *
 * @author Allan Lykke Christensen
 */
@Stateless
public class OutletFacadeBean implements OutletFacadeLocal {

    private static final Logger logger = Logger.getLogger(OutletFacadeBean.class.getName());

    @EJB private DaoServiceLocal daoService;

    @EJB private ConfigurationServiceLocal cfgService;

    @EJB private UserServiceLocal userService;

    @EJB private NotificationServiceLocal notificationService;

    @EJB private NewsItemFacadeLocal newsItemFacade;

    @EJB private SearchEngineLocal searchEngine;

    @EJB private SystemFacadeLocal systemFacade;

    @Resource private SessionContext ctx;

    /**
     * Creates a new instance of link OutletFacadeBean}.
     */
    public OutletFacadeBean() {
    }

    /** {@inheritDoc} */
    @Override
    public Outlet createOutlet(Outlet outlet) {
        return daoService.create(outlet);
    }

    /** {@inheritDoc} */
    @Override
    public List<Outlet> findAllOutlets() {
        return daoService.findAll(Outlet.class);
    }

    /** {@inheritDoc} */
    @Override
    public Outlet findOutletById(Long id) throws DataNotFoundException {
        return daoService.findById(Outlet.class, id);
    }

    /** {@inheritDoc } */
    @Override
    public Outlet updateOutlet(Outlet outlet) {
        return daoService.update(outlet);
    }

    /** {@inheritDoc } */
    @Override
    public void deleteOutletById(Long id) {
        daoService.delete(Outlet.class, id);
    }

    /** {@inheritDoc } */
    @Override
    public Department createDepartment(Department department) {
        return daoService.create(department);
    }

    /** {@inheritDoc } */
    @Override
    public Department findDepartmentById(Long id) throws DataNotFoundException {
        return daoService.findById(Department.class, id);
    }

    /** {@inheritDoc } */
    @Override
    public void updateDepartment(Department department) {
        daoService.update(department);
    }

    /** {@inheritDoc } */
    @Override
    public void deleteDepartment(Long id) {
        //TODO: Determine what to do about news item current in this department
        daoService.delete(Department.class, id);
    }

    /** {@inheritDoc } */
    @Override
    public Edition createEdition(Edition edition) {
        return daoService.create(edition);
    }

    /** {@inheritDoc } */
    @Override
    public Edition findEditionById(long id) throws DataNotFoundException {
        return daoService.findById(Edition.class, id);
    }

    /** {@inheritDoc } */
    @Override
    public List<Edition> findEditionByOutletAndDate(long outletId, Calendar date) {
        Calendar startDate = Calendar.getInstance();
        Calendar endDate = Calendar.getInstance();
        startDate.setTime(date.getTime());
        startDate.set(Calendar.HOUR_OF_DAY, 0);
        startDate.set(Calendar.MINUTE, 0);
        startDate.set(Calendar.SECOND, 0);

        endDate.setTime(date.getTime());
        endDate.set(Calendar.HOUR_OF_DAY, 23);
        endDate.set(Calendar.MINUTE, 59);
        endDate.set(Calendar.SECOND, 59);
        Outlet outlet = null;
        try {
            outlet = findOutletById(outletId);
        } catch (DataNotFoundException ex) {
            return new ArrayList<Edition>();
        }
        Map params = QueryBuilder.with("outlet", outlet).
                and("start_date", startDate).
                and("end_date", endDate).parameters();

        return daoService.findWithNamedQuery(Edition.FIND_BY_OUTLET_AND_DATE, params);
    }

    /** {@inheritDoc} */
    @Override
    public List<Edition> findEditionsByStatus(boolean status, Outlet outlet) {
        Map<String, Object> params = QueryBuilder.with("outlet", outlet).and("status", status).parameters();
        return daoService.findWithNamedQuery(Edition.FIND_BY_STATUS, params);
    }

    /** {@inheritDoc} */
    @Override
    public Edition findNextEdition(Outlet outlet) throws DataNotFoundException {
        Calendar now = Calendar.getInstance();
        Edition match = null;
        List<Edition> candidates = findEditionsByStatus(true, outlet);

        for (Edition candidate : candidates) {
            if (match == null) {
                match = candidate;
            }
            if (candidate.getPublicationDate().after(now) && match.getPublicationDate().after(candidate.getPublicationDate())) {
                match = candidate;
            }
        }

        if (match == null) {
            throw new DataNotFoundException();
        }
        return match;
    }

    @Override
    public List<Edition> findEditionsByDate(Outlet outlet, Calendar date) {
        // 1. Check if there are any editions in the database matching the date and outlet
        Calendar startDate = CalendarUtils.getStartOfDay(date);
        Calendar endDate = CalendarUtils.getEndOfDay(date);

        Map<String, Object> params = QueryBuilder.with("outlet", outlet).and("start_date", startDate).and("end_date", endDate).parameters();
        List<Edition> editions = daoService.findWithNamedQuery(Edition.FIND_BY_OUTLET_AND_DATE, params);

        // 2. Generate editions based on pattern.
        List<EditionPattern> relavantPatterns = new ArrayList<EditionPattern>();
        for (EditionPattern pattern : outlet.getEditionPatterns()) {
            if (pattern.getDay() == date.get(java.util.Calendar.DAY_OF_WEEK)) {
                boolean add = true;
                for (Edition ex : editions) {
                    if (ex.getPublicationDate().get(java.util.Calendar.HOUR_OF_DAY) == pattern.getStartHour() && ex.getPublicationDate().get(java.util.Calendar.MINUTE) == pattern.getStartMinute()) {
                        add = false;
                    }
                }

                if (add) {
                    relavantPatterns.add(pattern);
                }
            }
        }

        for (EditionPattern relavantPattern : relavantPatterns) {
            Calendar start = Calendar.getInstance();
            start.set(Calendar.HOUR_OF_DAY, relavantPattern.getStartHour());
            start.set(Calendar.MINUTE, relavantPattern.getStartMinute());
            start.set(Calendar.SECOND, 0);
            start.set(Calendar.MILLISECOND, 0);

            Calendar end = Calendar.getInstance();
            end.set(Calendar.HOUR_OF_DAY, relavantPattern.getEndHour());
            end.set(Calendar.MINUTE, relavantPattern.getEndMinute());
            end.set(Calendar.SECOND, 0);
            end.set(Calendar.MILLISECOND, 0);

            Edition edition = new Edition();
            edition.setOutlet(outlet);
            edition.setPublicationDate(start);
            edition.setExpirationDate(end);
            edition.setOpen(true);
            edition.setVolume(0);
            edition.setNumber(0);
            editions.add(edition);
        }

        return editions;
    }

    /** {@inheritDoc} */
    @Override
    public Edition updateEdition(Edition edition) {

        edition = daoService.update(edition);
        if (!edition.isOpen()) {
            String uid = ctx.getCallerPrincipal().getName();
            UserAccount ua = null;
            try {
                ua = userService.findById(uid);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            List<OutletEditionAction> actions = edition.getOutlet().getEditionActions();


            PluginContext pluginContext = systemFacade.getPluginContext();

            if (actions.isEmpty()) {
                Logger.getLogger(OutletFacadeBean.class.getName()).log(Level.INFO, "No actions to execute for edition");

            }

            for (OutletEditionAction action : actions) {
                Logger.getLogger(OutletFacadeBean.class.getName()).log(Level.INFO, "Executing {0} ({1})", new Object[]{action.getLabel(), action.getActionClass()});
                try {
                    EditionAction editionAction = action.getAction();
                    editionAction.execute(pluginContext, edition, action, ua);
                } catch (EditionActionException ex) {
                    Logger.getLogger(OutletFacadeBean.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }

        return edition;
    }

    /** {@inheritDoc } */
    @Override
    public void deleteEdition(Long id) {
        daoService.delete(Edition.class, id);
    }

    /** {@inheritDoc } */
    @Override
    public Section findSectionById(Long id) throws DataNotFoundException {
        return daoService.findById(Section.class, id);
    }

    /** {@inheritDoc } */
    @Override
    public Section createSection(Section section) {
        return daoService.create(section);
    }

    /** {@inheritDoc } */
    @Override
    public Section updateSection(Section section) {
        return daoService.update(section);
    }

    /** {@inheritDoc} */
    @Override
    public void deleteSection(Long id) throws EntityReferenceException {
        // TODO: Check if in use
        daoService.delete(Section.class, id);
    }

    /** {@inheritDoc} */
    @Override
    public OutletEditionAction createOutletAction(OutletEditionAction action) {
        return daoService.create(action);
    }

    /** {@inheritDoc} */
    @Override
    public OutletEditionAction updateOutletAction(OutletEditionAction action) {
        return daoService.update(action);
    }

    /** {@inheritDoc} */
    @Override
    public void deleteOutletActionById(Long id) {
        daoService.delete(OutletEditionAction.class, id);
    }

    /** {@inheritDoc} */
    @Override
    public void index(Long id) {
        try {
            Outlet outlet = findOutletById(id);
            Map<String, Object> params = QueryBuilder.with("outlet", outlet).and("state", outlet.getWorkflow().getEndState()).parameters();
            List<NewsItem> results = daoService.findWithNamedQuery(NewsItem.FIND_BY_OUTLET_AND_STATE, params);
            for (NewsItem newsItem : results) {
                try {
                    searchEngine.index(newsItem);
                } catch (SearchEngineIndexingException ex) {
                    logger.log(Level.SEVERE, "Could not index news item #{0}. {1}", new Object[]{newsItem.getId(), ex.getMessage()});
                }
            }
        } catch (DataNotFoundException ex) {
            logger.log(Level.FINE, ex.getMessage());
        }
    }

    /** {@inheritDoc} */
    @Override
    public void deleteEditionPatternById(Long id) {
        daoService.delete(EditionPattern.class, id);
    }

    /** {@inheritDoc} */
    @Override
    public EditionPattern createEditionPattern(EditionPattern editionPattern) {
        return daoService.create(editionPattern);
    }

    /** {@inheritDoc} */
    @Override
    public EditionPattern updateEditionPattern(EditionPattern editionPattern) {
        return daoService.update(editionPattern);
    }
}
